package com.example.context;

import com.example.annotation.MyAutowired;
import com.example.annotation.MyBean;
import com.example.service.IUserService;
import com.example.service.impl.UserServiceImpl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 手写一个IOC容器
 */
public class MyApplicationContext<T> {

    private Map<Class, Object> beanFactory = new ConcurrentHashMap<>();

    private String filePath;

    public T getBean(Class clazz) {

        return (T) beanFactory.get(clazz);
    }

    public void initContext() {
        //通过代码主动初始化
        beanFactory.put(IUserService.class, new UserServiceImpl());

        // 不主流方式
        // 通过bean.config + 反射机制 进行容器初始化
        //getResourceAsStream用法：https://www.cnblogs.com/macwhirr/p/8116583.html
        InputStream resource = MyApplicationContext.class.getClassLoader()
                .getResourceAsStream("config/bean.config");
        try {
            Properties properties = new Properties();
            properties.load(resource);
            Set<Object> keys = properties.keySet();
            for (Object key : keys) {
                properties.put(Class.forName(key.toString()),
                        Class.forName(properties.getProperty(key.toString())));
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void load() {
        filePath = MyApplicationContext.class.getClassLoader()
                .getResource("").getFile();
        System.out.println(filePath);
        loadOne(new File(filePath));
        System.out.println(filePath);

        //对实例中引用的遍历 进行初始化
        assignObject();

    }

    private void assignObject() {

        for (Map.Entry<Class, Object> entry : beanFactory.entrySet()) {
            Object instance = entry.getValue();

            //此处不可以用 Class<?> aClass = entry.getKey(); 因为这样会拿不到declareFields
            Class<?> aClass = instance.getClass();
            //获取当前类引用的字段
            Field[] declareFields = aClass.getDeclaredFields();

            for (Field field : declareFields) {

                MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
                if (myAutowired == null) {
                    continue;
                }

                try {
                    //设置私有变量 可读
                    field.setAccessible(true);
                    field.set(instance, beanFactory.get(field.getType()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    /**
     * 递归获取类文件
     *
     * @param fileParent
     */
    private void loadOne(File fileParent) {
        if (fileParent.isDirectory()) {
            File[] childrenFiles = fileParent.listFiles();
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            for (File child : childrenFiles) {
                if (child.isDirectory()) {
                    //如果是个文件夹，就继续调用该方法
                    loadOne(child);
                } else {
                    //通过文件历经转变成全类名，先把绝对路径部分去掉
                    String pathWithClass = child.getAbsolutePath().substring(filePath.length());

                    //选中class文件
                    if (pathWithClass.contains(".class")) {
                        //去掉.class后缀，并且把 \ 替换成 .
                        String fullName = pathWithClass.replaceAll("\\/", ".")
                                .replace(".class", "");
                        try {
                            Class<?> aClass = Class.forName(fullName);
                            //把非接口的类实例化放在map中
                            if (!aClass.isInterface()) {
                                Object instance = aClass.newInstance();
                                boolean myBeanAnnotationStatus = myBeanAnnotationStatus(aClass);
                                //放入对应的接口名和实例
                                if (myBeanAnnotationStatus && aClass.getInterfaces().length > 0) {
                                    beanFactory.put(aClass.getInterfaces()[0], instance);
                                }
                            }
                        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                            System.out.println(fullName);
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 判断是否应该被我们的自定义MyBean注解修饰
     *
     * @param aClass
     * @return
     */
    private boolean myBeanAnnotationStatus(Class<?> aClass) {
        MyBean myBean = aClass.getAnnotation(MyBean.class);
        if (myBean != null) {
            return true;
        }
        return false;
    }

}
